# vPresent is an immersive presentation creation and display application.
# Copyright (C) 2007 by Infiscape Corporation
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import string
import sys
import opendocument
import opendocument.config as config
import opendocument.drawing as drawing
import opendocument.office as office
import opendocument.text as text
import weakref
pres_base = opendocument.bases.presentation
od = opendocument

class Presentation(opendocument.Document):
   '''
   OpenDocument v1.1 presentation document.  This is the product created by
   PresentationBuilder.
   '''
   # Add callables to this object for supported container types. The
   # callables have the name "add<Container Type>" and take as a single
   # argument the container document node object.
   container_types = ['ConfigItemMapEntry', 'NamedConfigItemMap',
                      'IndexedConfigItemMap']

   def __init__(self, undoStack, parent = None):
      opendocument.Document.__init__(self, undoStack, parent)
      self.settings  = office.Settings({})
      self.docMeta   = None
      self.docStyles = None
      self.content = None
      self.stylesDB  = {}
      self.pageStyleDB = {}
      self.fillImageDB = {}

      self.containerStack = []
      self.curContainer   = None

      for t in Presentation.container_types:
         self.__dict__['add%s' % t] = self.__handleContainer

   def __getstate__(self):
      dict = self.__dict__.copy()

      # We cannot pickle the undo stack for this document, and there is no
      # point in doing so anyway.
      del dict['_Document__undoStack']
      for t in Presentation.container_types:
         del dict['add%s' % t]
      return dict

   def refresh(self):
      # Get the presentation node and add a node filter
      pres_node = self.content.find('./office:body/office:presentation')
      assert pres_node is not None
      self.pages = od.NodeFilter(pres_node, 'draw:page')

      # Find all graphic styles.
      auto_styles_node = self.content.find('./office:automatic-styles')
      self.materials = od.NodeFilter(auto_styles_node, 'style:style', {'style:family':'graphic',})

      self.pageStyles = od.NodeFilter(auto_styles_node, 'style:style', {'style:family':'page3d',})

      # Find all textures that we can apply.
      styles_node = self.docStyles.find('./office:styles')
      assert styles_node is not None
      self.textures = od.NodeFilter(styles_node, 'draw:fill-image')

   def __handleContainer(self, container):
      '''
      Adds the given container as a child of the current container and then
      sets the current container to be the given container.

      @post self.curContainer is container.
      '''
      self.curContainer.addChild(container)
      self.__setNewContainer(container)

   def addGenericNode(self, node):
      if self.curContainer is None:
         print "ERROR: No container for node", node
      self.curContainer.addChild(node)
      if node.acceptsChildren():
         self.__setNewContainer(node)

   def getMimeType():
      return 'application/vnd.oasis.opendocument.presentation'

   getMimeType = staticmethod(getMimeType)

   def getContentType():
      return 'presentation'

   getContentType = staticmethod(getContentType)

   def getSettings(self):
      return self.settings

   def getMetadata(self):
      return self.docMeta[0]

   def getStyles(self):
      return self.docStyles

   def getContent(self):
      return self.content

   def getChildren(self):
      return self.children

   def applySettings(self, env):
      for s in self.settings.getChildren():
         if s.getName().startswith('vpresent:'):
#            if s.getName() == 'vpresent:view-settings':
#            if s.getName() == 'vpresent:configuration-settings':
            self.__applyContainedSettings(env, s)

   def __applyContainedSettings(self, env, settingsContainer):
      for s in settingsContainer.getChildren():
         if isinstance(s, config.Container):
            self.__applyContainedSettings(env, s)
         else:
            self.__applySetting(env, s)

   def __applySetting(self, env, setting, name = None):
      pass
#      print setting.getName()
#      print setting.getValue()

   def addConfigItemSet(self, configItemSet):
      if self.curContainer is None:
         self.settings.addChild(configItemSet)
         self.__setNewContainer(configItemSet)
      else:
         self.__handleContainer(configItemSet)

   def addDocumentStyles(self, docStyles):
      self.docStyles = docStyles
      self.__setNewContainer(docStyles)

   def addDocumentMeta(self, docMeta):
      self.docMeta = docMeta
      self.__setNewContainer(docMeta)

   def addDocumentContent(self, docContent):
      self.content = docContent
      self.__setNewContainer(docContent)

   def removeStyle(self, style):
      #print "Removing style:", style.style_name
      sn = style.style_name
      if self.stylesDB.has_key(sn):
         del self.stylesDB[sn]

      if self.pageStyleDB.has_key(sn):
         del self.pageStyleDB[sn]

   def addStyle(self, style):
      if style.style_family == 'graphic':
         #print "Adding style:", style.style_name
         self.stylesDB[style.style_name] = style
      elif style.style_family == 'page3d':
         self.pageStyleDB[style.style_name] = style

   def generatePageStyleName(self):
      name = "pagestyle0"
      idx = 0
      while self.pageStyleDB.has_key(name):
         idx += 1
         name = "pagestyle%d"%(idx)
      return name

   def getPageStyle(self, styleName):
      return self.pageStyleDB.get(styleName, None)

   def getMaterial(self, materialName):
      #print "Getting style [%s] %s" % (materialName, self.stylesDB.get(materialName, None))
      return self.stylesDB.get(materialName, None)

   def generateMaterialName(self):
      name = "material0"
      idx = 0
      while self.stylesDB.has_key(name):
         idx += 1
         name = "material%d"%(idx)
      return name

   def addFillImage(self, image):
      #print "Add fill image:", image.draw_name
      self.fillImageDB[image.draw_name] = image

   def removeFillImage(self, image):
      if self.fillImageDB.has_key(image.draw_name):
         del self.fillImageDB[image.draw_name]

   def getFillImage(self, drawName, rebuild=False):
      fill_image = self.fillImageDB.get(drawName, None)
      #if fill_image is not None and False == fill_image._built:
      #   fill_image._rebuild()
      return fill_image

   def generateFillImageName(self):
      name = "fillimage0"
      idx = 0
      while self.fillImageDB.has_key(name):
         idx += 1
         name = "fillimage%d"%(idx)
      return name

   def addEventListeners(self, listeners):
      self.curContainer.setEventListeners(listeners)

   def addEventListener(self, listener):
      self.curContainer.getEventListeners().addChild(listener)

   def __setNewContainer(self, newContainer):
      '''
      Changes the current container object to be the given object.  If there
      is currently an active container object, it is pushed onto the top of
      the container stack.

      @pre The new container accepts children.
      @post self.curContainer refers to newContainer.  If self.curContainer was
            not None prior to invocation of this method, then the last element
            in self.containerStack refers to the object previously pointed to
            by self.curContainer.  Otherwise, there is no change to
            self.containerStack.
      '''
      if not newContainer.acceptsChildren():
         print "Not a container %s" % newContainer
         assert newContainer.acceptsChildren()

      if self.curContainer is not None:
         self.containerStack.append(self.curContainer)

      self.curContainer = newContainer

   def closeContainer(self):
      '''
      Closes the current container and makes the top of the container stack
      the new current container.  If the container stack is empty, then there
      is no active container object.

      @post self.curContainer holds one less entry. self.curContainer refers
            to the top of the container stack prior to the invocation of this
            method or None if the container stack was empty.
      '''
      stack_len = len(self.containerStack)
      if stack_len > 0:
         self.curContainer = self.containerStack.pop()
      else:
         self.curContainer = None

class Notes(pres_base.Notes):
   __metaclass__ = od.OpenDocumentMeta
   childTypes = ['draw:page-thumbnail', 'draw:frame', 'office:forms']
   def __init__(self, attrs):
      pres_base.Notes.__init__(self, attrs)
